<?php


namespace App\Services;

use App\Exceptions\ApiException;
use App\Models\Coins;
use App\Models\OtcAccount;
use App\Models\OtcCoinlist;
use App\Models\OtcEntrust;
use App\Models\OtcOrder;
use App\Models\User;
use App\Models\UserPayment;
use App\Models\UserWallet;
use App\Services\ExchangeRateService\ExchangeRateService;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use App\Models\UserWalletLog;

class OtcService
{
    public function otcTicker()
    {
        return OtcCoinlist::query()->where('status', 1)->get();
    }

    public function tradingEntrusts($user, $params)
    {
        $builder = OtcEntrust::query()->with('user')->where(['coin_name' => $params['virtual_coin'], 'side' => $params['side']])->where('status', 1);

        if (!empty($user)) {
            $builder->where('user_id', '!=', $user['user_id']);
        }

        if ($params['pay_type'] != 'all') {
            $builder->whereRaw('FIND_IN_SET(?,pay_type)', [$params['pay_type']]);
        }

        return $builder->paginate();
    }
    
    
    public function otcWalletLogs($user, $params)
    {
        $builder = UserWalletLog::query()
            ->where('user_id', $user['user_id'])
            ->where('rich_type', 'usable_balance')
            ->where('account_type', UserWallet::otc_account);

        return $builder->orderByDesc('id')->paginate();
    }

    public function storeBuyEntrust($user, $params)
    {
        $pair = OtcCoinlist::query()->where('coin_name', $params['virtual_coin'])->first();
        if (empty($pair)) throw new ApiException();
        if (($can_store = $pair->can_store()) !== true) throw new ApiException($can_store);

        // 检测收款方式
        $payments = json_decode($params['pay_type'], true);
        if (empty($payments)) throw new ApiException('收款方式不能为空');
        foreach ($payments as $payment) {
            $is_exist = UserPayment::query()
                ->where('user_id', $user['user_id'])
                ->where('pay_type', $payment)
                ->exists();
            if (!$is_exist) throw new ApiException('未绑定该收款方式');
        }

        $amount = $params['amount'];

        $overtime = $pair['max_register_time'];
        if ($overtime == 0) {
            $overed_time = 0;
        } else {
            $overed_time = Carbon::now()->addHours(intval($overtime))->toDateTimeString();
        }

        DB::beginTransaction();
        try {

            $entrust = OtcEntrust::query()->create([
                'user_id' => $user['user_id'],
                'side' => $params['side'],
                'order_sn' => get_order_sn('otc'),
                'coin_id' => $pair['coin_id'],
                'coin_name' => $pair['coin_name'],
                'min_num' => $params['min_num'] ?? null,
                'max_num' => $params['max_num'] ?? null,
                'pay_type' => implode(',', $payments),
                'note' => $params['note'] ?? null,
                'publish_time' => time(),
                'price' => $params['price'],
                'amount' => $amount,
                'cur_amount' => $amount,
                'overed_at' => $overed_time,
            ]);

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            throw new ApiException($e->getMessage());
        }

        return $entrust;
    }

    public function storeSellEntrust($user, $params)
    {
        $pair = OtcCoinlist::query()->where('coin_name', $params['virtual_coin'])->first();
        if (empty($pair)) throw new ApiException();
        if (($can_store = $pair->can_store()) !== true) throw new ApiException($can_store);

        // 检测收款方式
        $payments = json_decode($params['pay_type'], true);
        if (empty($payments)) throw new ApiException('收款方式不能为空');
        foreach ($payments as $payment) {
            $is_exist = UserPayment::query()
                ->where('user_id', $user['user_id'])
                ->where('pay_type', $payment)
                ->exists();
            if (!$is_exist) throw new ApiException('未绑定该收款方式');
        }

        //法币账户
        $account = OtcAccount::query()->where(['user_id' => $user['user_id'], 'coin_name' => $pair['coin_name']])->first();
        if (empty($account)) throw new ApiException('账户类型错误');
        $balance = $account->usable_balance;
        $amount = $params['amount'];
        if ($balance < $amount) throw new ApiException('余额不足');

        $overtime = $pair['max_register_time'];
        if ($overtime == 0) {
            $overed_time = 0;
        } else {
            $overed_time = Carbon::now()->addHours(intval($overtime))->toDateTimeString();
        }

        DB::beginTransaction();
        try {

            $entrust = OtcEntrust::query()->create([
                'user_id' => $user['user_id'],
                'side' => $params['side'],
                'order_sn' => get_order_sn('otc'),
                'coin_id' => $pair['coin_id'],
                'coin_name' => $pair['coin_name'],
                'min_num' => $params['min_num'] ?? null,
                'max_num' => $params['max_num'] ?? null,
                'pay_type' => implode(',', json_decode($params['pay_type'], true)),
                'note' => $params['note'] ?? null,
                'publish_time' => time(),
                'price' => $params['price'],
                'amount' => $amount,
                'cur_amount' => $amount,
                'overed_at' => $overed_time,
            ]);

            //扣除用户可用资产 冻结
            $user->update_wallet_and_log($account['coin_id'], 'usable_balance', -$amount, UserWallet::otc_account, 'store_otc_sell_entrust');
            $user->update_wallet_and_log($account['coin_id'], 'freeze_balance', $amount, UserWallet::otc_account, 'store_otc_sell_entrust');

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            throw new ApiException($e->getMessage());
        }
        return $entrust;
    }

    public function storeOrder($user, $params)
    {
        $entrust = OtcEntrust::query()->find($params['entrust_id']);
        if (empty($entrust)) throw new ApiException('委托不存在');
        if ($user['user_id'] == $entrust['user_id']) throw new ApiException('不能和自己进行交易');
        //        if($entrust['side'] == $params['trans_type']) throw new ApiException('参数错误');

        $trans_type = $entrust['side'] == 1 ? 2 : 1;
        // 判断买卖双方的交易方式
        $pay_type_arr = is_array($entrust['pay_type']) ? $entrust['pay_type'] : explode(',', $entrust['pay_type']);
        if (!in_array($params['pay_type'], $pay_type_arr)) {
            throw new ApiException('支付方式不匹配');
        }

        // 检测收款方式
        $is_exist = UserPayment::query()
            ->where('user_id', $user['user_id'])
            ->where('pay_type', $params['pay_type'])
            ->exists();
        if (!$is_exist) throw new ApiException('未绑定该收款方式');


        $amount = $params['amount'];
        if ($entrust['cur_amount'] < $amount) throw new ApiException('下单数量不得大于剩余数量');
        if (!empty($entrust['min_num']) && $entrust['min_num'] != 0 && $entrust['min_num'] > $amount) throw new ApiException('下单数量不能小于最小限量');
        if (!empty($entrust['max_num']) && $entrust['max_num'] != 0 && $entrust['max_num'] < $amount) throw new ApiException('下单数量不能大于最大限量');

        $overtime = get_setting_value('otc_order_overed', 'otc', 15);
        if ($overtime == 0) {
            $overed_time = 0;
        } else {
            $overed_time = Carbon::now()->addMinutes(intval($overtime))->toDateTimeString();
        }

        DB::beginTransaction();
        try {
            $order_data = [
                'trans_type' => $trans_type,
                'order_sn' => get_order_sn('od'),
                'user_id' => $user['user_id'],
                'other_uid' => $entrust['user_id'],
                'entrust_id' => $entrust['id'],
                'coin_id' => $entrust['coin_id'],
                'coin_name' => $entrust['coin_name'],
                'amount' => $amount,
                'pay_type' => $params['pay_type'],
                'price' => $entrust['price'],
                'money' => $params['amount'] * $entrust['price'],
                'order_time' => time(),
                'status' => OtcOrder::status_wait_pay,
                'overed_at' => $overed_time,
            ];
            $order = OtcOrder::query()->create($order_data);

            if ($trans_type == 2) {
                $user->update_wallet_and_log($order['coin_id'], 'usable_balance', -$amount, UserWallet::otc_account, 'store_otc_order');
                $user->update_wallet_and_log($order['coin_id'], 'freeze_balance', $amount, UserWallet::otc_account, 'store_otc_order');
            }

            $entrust->update([
                'cur_amount' => PriceCalculate($entrust['cur_amount'], '-', $amount, 6),
                'lock_amount' => PriceCalculate($entrust['lock_amount'], '+', $amount, 6),
            ]);

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            throw new ApiException($e->getMessage());
        }

        return $order;
    }

    public function myEntrusts($user_id, $params)
    {
        $builder = OtcEntrust::query()->where('user_id', $user_id);

        if (!empty($params['side'])) {
            $builder->where('side', $params['side']);
        }
        if (!empty($params['status'])) {
            $builder->where('status', $params['status']);
        }

        return $builder->orderByDesc('id')->paginate();
    }

    public function myOrders($user_id, $params)
    {
        $builder = OtcOrder::query();

        if (!empty($params['type'])) {
            $type = $params['type'];
            if ($type == 2) {
                $builder->where(['trans_type' => 1, 'user_id' => $user_id]);
            } elseif ($type == 1) {
                $builder->where(['trans_type' => 2, 'user_id' => $user_id]);
            } elseif ($type == 3) {
                $builder->where(['trans_type' => 2, 'other_uid' => $user_id]);
            } else {
                $builder->where(['trans_type' => 1, 'other_uid' => $user_id]);
            }
        }
        if (!empty($params['status'])) {
            if ($params['status'] != 99) {
                $builder->where('status', $params['status']);
            }
        }

        return $builder->orderByDesc('id')->paginate();
    }

    public function orderDetail($user_id, $params)
    {
        return OtcOrder::query()->where('id', $params['order_id'])->where(function ($q) use ($user_id) {
            $q->where('user_id', $user_id)->orWhere('other_uid', $user_id);
        })->firstOrFail();
    }

    public function cancelEntrust($user_id, $params)
    {
        $entrust = OtcEntrust::query()->where(['user_id' => $user_id, 'id' => $params['entrust_id']])->firstOrFail();
        if (!$entrust->canCancel()) {
            throw new ApiException('当前委托不可撤销');
        }

        DB::beginTransaction();
        try {

            $entrust->update(['status' => OtcEntrust::status_canceled]);

            if ($entrust['side'] == 2) {
                // 退回剩余资金
                $user = User::query()->findOrFail($user_id);
                $user->update_wallet_and_log($entrust['coin_id'], 'usable_balance', $entrust['cur_amount'], UserWallet::otc_account, 'cancelOtcEntrust');
                $user->update_wallet_and_log($entrust['coin_id'], 'freeze_balance', -$entrust['cur_amount'], UserWallet::otc_account, 'cancelOtcEntrust');
            }

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }

        return $entrust;
    }

    public function cancelOrder($user_id, $params)
    {
        $order = OtcOrder::query()->where(['id' => $params['order_id']])->firstOrFail();
        if (!$order->canCancel()) {
            throw new ApiException('当前订单不可撤销');
        }
        if ($order['user_id'] != $user_id && $order['other_uid'] != $user_id) {
            throw new ApiException('非法操作');
        }

        DB::beginTransaction();
        try {

            $order->update(['status' => OtcEntrust::status_canceled]);

            $amount = $order['amount'];
            $entrust = $order->entrust;
            $entrust->update([
                'cur_amount' => PriceCalculate($entrust['cur_amount'], '+', $amount, 6),
                'lock_amount' => PriceCalculate($entrust['lock_amount'], '-', $amount, 6),
            ]);

            if ($order['trans_type'] == 2) {
                $seller = $order->getSeller();
                $seller->update_wallet_and_log($order['coin_id'], 'usable_balance', $amount, UserWallet::otc_account, 'cancel_otc_order');
                $seller->update_wallet_and_log($order['coin_id'], 'freeze_balance', -$amount, UserWallet::otc_account, 'cancel_otc_order');
            }

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }

        return $order;
    }

    public function confirmPaidOrder($user_id, $params)
    {
        $order = OtcOrder::query()->where('id', $params['order_id'])->firstOrFail();
        if (($checkRes = $order->canConfirmPaid()) !== true) throw new ApiException($checkRes);

        if ($order['trans_type'] == 1) {
            if ($user_id != $order['user_id']) throw new ApiException('非法操作');
        } else {
            if ($user_id != $order['other_uid']) throw new ApiException('非法操作');
        }

        DB::beginTransaction();
        try {

            $order->update(['status' => OtcOrder::status_wait_confirm, 'paid_img' => $params['paid_img'], 'pay_time' => time()]);

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }

        return $order;
    }

    public function confirmOrder($user_id, $params)
    {
        $order = OtcOrder::query()->where('id', $params['order_id'])->firstOrFail();
        if (($checkRes = $order->canConfirmOrder()) !== true) throw new ApiException($checkRes);

        if ($order['trans_type'] == 1) {
            if ($user_id != $order['other_uid']) throw new ApiException('非法操作');
        } else {
            if ($user_id != $order['user_id']) throw new ApiException('非法操作');
        }

        DB::beginTransaction();
        try {

            // 更新订单
            $order->update(['status' => OtcOrder::status_completed, 'deal_time' => time()]);

            // 更新委托
            $entrust = $order->entrust;
            $entrust->update(['lock_amount' => $entrust['lock_amount'] - $order['amount']]);

            //买家入账
            $buyer = $order->getBuyer();
            $buyer->update_wallet_and_log($order['coin_id'], 'usable_balance', $order['amount'], UserWallet::otc_account, 'confirmOtcOrder');

            // 卖家冻结金额减少
            $seller = $order->getSeller();
            $seller->update_wallet_and_log($order['coin_id'], 'freeze_balance', -$order['amount'], UserWallet::otc_account, 'confirmOtcOrder');

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }

        return $order;
    }

    public function notConfirmOrder($user_id, $params)
    {
        $order = OtcOrder::query()->where('id', $params['order_id'])->firstOrFail();
        if (($checkRes = $order->canConfirmOrder()) !== true) throw new ApiException($checkRes);

        if ($order['trans_type'] == 1) {
            if ($user_id != $order['other_uid']) throw new ApiException('非法操作');
        } else {
            if ($user_id != $order['user_id']) throw new ApiException('非法操作');
        }

        DB::beginTransaction();
        try {

            // 更新订单
            $order->update(['status' => OtcOrder::status_appealing, 'appeal_status' => OtcOrder::appeal_status_wait, 'appeal_time' => time()]);

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }

        return $order;
    }
}
